<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <script type="text/javascript" src="../jsunit/app/jsUnitCore.js"></script>
    <script type="text/javascript" src="../../src/objx.test.js"></script>
    <script type="text/javascript" src="../../src/objx.js"></script>
    <script type="text/javascript">
    
        function test_simpleClass()
        {
        
            var c = objx.Class({
                method: function(){}
            });
        
            // ensure the type 't' is set
            assertEquals("c", c.objx.t);
            
            var i1 = new c();
            var i2 = new c();
            
            assertNotUndefined(i1.method);
            assertNotUndefined(i2.method);
        
        }
        
        function test_fieldsExistInInstances()
        {
        
        	var c = objx.Class({
        		field: 123,
        		method: function(){}
        	});
        	
        	var i = new c();
        	
        	assertEquals("Field from class definition was not present and equal in instance.", 123, i.field);
        
        }
        
        function test_inheritance()
        {
        
            var Animal = objx.Class(objx.abstract({
                speak: function() { return 1; }    
            }));
            
            var Dog = objx.Class(Animal, {});
            
            var myDoggy = new Dog();
            assertNotUndefined("speak() method expected to be inherited from base class.", myDoggy.speak);
            assertEquals(1, myDoggy.speak());
        
            // ensure we have access to the original base methods too
            var Snake = objx.Class(Animal, {
                speak: function(){
                    return 123;
                }
            });
            
            var mySnake = new Snake();
            assertEquals(123, mySnake.speak());
            assertEquals(1, mySnake.base_speak());
        
        }
        
        function test_ensureSystemMethodsArentInherited()
        {
        
        
            var Animal = objx.Class(objx.abstract({    speak: function() { return 1; }    }));
            var Dog = objx.Class(Animal, {});
            var Snake = objx.Class(Animal, { speak: function(){ return 123; } });
            
            var myDoggy = new Dog();

            // make sure no 'base_' versions of the class methods exist
            for (var property in objx.classMethods)
            {
                assertUndefined("System Class Methods should not be treated as normal methods and therefore 'base_" + property + "' shouldn't be defined but is.", 
                    myDoggy["base_" + property]);        
            }

        }
        
        function test_inheritance_context()
        {
        
            var Animal = objx.Class({
                speak: function() { return this.value; }    
            });
            
            var Dog = objx.Class(Animal, {});
            
            var myAnimal = new Animal();
            var myDoggy  = new Dog();
            
            myAnimal.value = "animal";
            myDoggy.value = "doggy";
            
            assertEquals("animal", myAnimal.speak());
            assertEquals("doggy", myDoggy.speak());
        
        }

        function test_exceptionThrown_whenClassCalledWithWrongArgs()
        {
            
            // ensure an exception is thrown when no arguments are passed to class()
            
            assertThrowsType("ArgumentException expected to be thrown when objx.Class() is called with no arguments.", 
                objx.exceptions.ArgumentException, function(){
                    var c = objx.Class();
                }
            );
            
//            assertThrows("Calling objx.Class() with no arguments should throw an exception.", function(){
//                var c = objx.Class();
//            });
            
            // ensure an exception is thrown when anything but an object is passed as the last argument
            assertThrowsType("ArgumentException expected when passing function as argument to class.", 
                objx.exceptions.ArgumentException, function(){
                    var c = objx.Class(function(){});
                }
            );
            assertThrowsType("ArgumentException expected when passing number as argument to class.", 
                objx.exceptions.ArgumentException, function(){
                    var c = objx.Class(123);
                }
            );
            assertThrowsType("ArgumentException expected when passing string as argument to class.", 
                objx.exceptions.ArgumentException, function(){
                    var c = objx.Class("string");
                }
            );
            assertThrowsType("ArgumentException expected when passing null as argument to class.", 
                objx.exceptions.ArgumentException, function(){
                    var c = objx.Class(null);
                }
            );
            
        }
        
        function test_exceptionThrown_multipleBaseClasses()
        {
        
            var BaseOne = objx.Class({});
            var BaseTwo = objx.Class({});

            assertThrowsType("ClassException expected when assing multiple classes to inherit from.", 
                objx.exceptions.ClassException, function(){
                    var InvalidClass = objx.Class(BaseOne, BaseTwo, {
                    });
                }
            );
        
        }
        

        
        var counter = 0;
        
        function test_initFunction(){
        
            var MyClass = objx.Class({
                init: function(c){
                    counter += c;
                }
            });
            
            counter = 0;
            
            assertEquals(0, counter);
            
            var MyInst = new MyClass(5);
            
            assertEquals(5, counter);
            
            var MySecondInst = new MyClass(10);
            
            assertEquals(15, counter);
        
        }
    
        function test_constructorInheritance()
        {
        
            counter = 0;
        
            var BaseClass = objx.Class({
                init: function(c){
                    counter += c;
                }
            });
            
            var ChildClass = objx.Class(BaseClass, {
                init: function(){
                    this.base_init(10);
                }
            });
            
            var instance = new ChildClass();
            
            assertEquals(10, counter);
        
        }
    
    
        function test_classesAreIndependant()
        {
        
            var class1 = objx.Class({
                init: function(){ return 1; }
            });
            
            var class2 = objx.Class({
                init: function(){ return 2; }
            });
            
            assertNotEquals(class1.prototype.init.toString(), class2.prototype.init.toString());
            
        }
        
        function test_UsingInterfaceDoesntStealInterfaceInitMethod()
        {
        
            var MyInterface = objx.interface({});
            var MyClass = objx.Class(MyInterface, {});
            
            assertNotEquals(objx.Interface.prototype.init.toString(), MyClass.prototype.init.toString());
        
        }
        
        function test_inheritanceExample_2()
        {
                    
            var BaseClass = objx.Class(objx.abstract({
                popup: function(m){ return "I say " + m }
            }));

            var ChildOne = objx.Class(BaseClass, {
                sayHello: function(){ return this.popup("Hello"); }
            });

            var myChildOne = new ChildOne();
            
            assertEquals("I say Hello", myChildOne.sayHello());

        }
        
        function test_classMethods()
        {
        
            var MyClass = objx.c({
                getDisplayName: function(){ return "Name"; }
            });
            
            var inst = new MyClass();
            
            assertNotUndefined("inst.actsAs", inst.actsAs);
            assertNotUndefined("inst.implements", inst.implements);
            assertNotUndefined("inst.observe", inst.observe);
            assertNotUndefined("inst.notify", inst.notify);
            
            assertNotUndefined("MyClass.actsAs", MyClass.actsAs);
            assertNotUndefined("MyClass.implements", MyClass.implements);
        
        }
        
        function test_classInstancesGetType()
        {
            
            var MyClass = objx.Class({});
            var MyInst = new MyClass();
            
            assertEquals("Class expected on Instance.type", MyClass, MyInst.type);
        
        }
        
        function test_baseMethodsInAbstractSubclass(){
        
        	var Base = AbstractClass({
        		myMethod: function(){ return 123; }
        	});
        	
        	var Sub = AbstractClass(Base, {
        		myMethod: function(){
        			return this.base_myMethod() + 1;
        		}
        	});
        	        	
        	assertEquals(Base.prototype.myMethod.toString(), Sub.prototype.base_myMethod.toString());
        	assertEquals(123, Base.prototype.myMethod());
        	assertEquals(123, Sub.prototype.base_myMethod());
        	assertEquals(124, Sub.prototype.myMethod());

        }
        
        function test_classDefiningItsOwnToString(){
        
        	var Animal = Class({
        		init: function(name){ this.name = name; },
        		toString: function(){
        			return "My name is " + this.name;
        		}
        	});
        	
        	var fido = new Animal("Fido");
        	
        	assertEquals("My name is Fido", fido.toString());
        
        }
        
        function test_newObjxObjectForEachInstance(){
        
        	var CommonAbstract = AbstractClass({});
        
        	var ClassOne = Class(CommonAbstract, {});
        	var ClassTwo = Class(CommonAbstract, {});
        	
        	var instanceOne = new ClassOne();
        	var instanceTwo = new ClassTwo();
        	
        	instanceOne.observe("test", objx.nf);
        	instanceTwo.observe("test", objx.nf);
        	
        	assertEquals(1, instanceOne.objx.ev.test.length);
        	assertEquals(1, instanceTwo.objx.ev.test.length);
        	
        	assertNotEquals("Two seperate instances should NOT share the same 'objx' instance.", instanceOne.objx, instanceTwo.objx);
        
        }
        
        function test_classProperties(){
        
        	// error message shortcut for tests
        	var msg = "";
        	
        	// make sure unique objects exist to represent Public, Private, ReadOnly and WriteOnly
        	msg = "New keyword objects expected to exist but don't. - ";
        	assertNotUndefined(msg + "objx.publicProperty", objx.publicProperty); 
        	assertNotUndefined(msg + "objx.privateProperty", objx.privateProperty); 
        	assertNotUndefined(msg + "objx.readOnlyProperty", objx.readOnlyProperty); 
        	assertNotUndefined(msg + "objx.writeOnlyProperty", objx.writeOnlyProperty); 

			// make sure the shortcuts exist too
			msg = "Shortcuts expected to exist for each proeprty - ";
        	assertNotUndefined(msg + "Public", Public); 
        	assertNotUndefined(msg + "Private", Private); 
        	assertNotUndefined(msg + "ReadOnly", ReadOnly); 
        	assertNotUndefined(msg + "WriteOnly", WriteOnly); 

			// make sure the objects always refer to the same instance
			msg = "Shortcuts expected to be a simple reference to their objx. brothers.";
			assertEquals(msg + " - Public", Public, objx.publicProperty);
			assertEquals(msg + " - Private", Private, objx.privateProperty);
			assertEquals(msg + " - ReadOnly", ReadOnly, objx.readOnlyProperty);
			assertEquals(msg + " - WriteOnly", WriteOnly, objx.writeOnlyProperty);

			msg = "objx.Property expected to be an objx Class";

			assertNotUndefined(msg, objx.Property);
			assertIsClass(msg, objx.Property);

			// make sure they are properties
			msg = "Property keywords should be instances of objx classes of type 'p' for 'property'. - ";

			assertNotUndefined(msg, Public.objx);
			assertNotUndefined(msg, Private.objx);
			assertNotUndefined(msg, ReadOnly.objx);
			assertNotUndefined(msg, WriteOnly.objx);

			assertEquals(msg, "p", Public.objx.t);
			assertEquals(msg, "p", Private.objx.t);
			assertEquals(msg, "p", ReadOnly.objx.t);
			assertEquals(msg, "p", WriteOnly.objx.t);

        	// create a sample class
        	var MyClass = Class({
        		
				firstName: Public,
				lastName: Public,
				age: ReadOnly,
				password: Private,
				salt: WriteOnly
        		
        	});
        	
        	// make sure the fields are in place
        	msg = "Class definition expected to have properties created as 'null' - ";
        	assertNotUndefined(msg + "MyClass.protytype.firstName", MyClass.prototype.firstName);
        	assertNotUndefined(msg + "MyClass.protytype.age", MyClass.prototype.age);
        	assertNotUndefined(msg + "MyClass.protytype.lastName", MyClass.prototype.lastName);
        	
        	// create an instance
        	var inst = new MyClass();
        
        	// make sure the instance gets the fields too
        	msg = "Instances expected to get the fields inherited when created. - ";
        	assertNotUndefined(msg + "inst.firstName", inst.firstName);
        	assertNotUndefined(msg + "inst.age", inst.age);
        	assertNotUndefined(msg + "inst.lastName", inst.lastName);
        
        	// make sure the instance has some getters for the public fields
        	msg = "Public properties and ReadOnly properties expected to have getter function. - ";
        	assertNotUndefined(msg + "inst.getFirstName", inst.getFirstName);
        	assertNotUndefined(msg + "inst.getLastName", inst.getLastName);
        	assertNotUndefined(msg + "inst.getAge", inst.getFirstName);
        	
        	// make sure the instance has some 'private' getters for the Private fields
        	msg = "Private properties expected to have _getter and _setter functions. - ";
        	assertNotUndefined(msg + "inst._getPassword", inst._getPassword);
        	assertNotUndefined(msg + "inst._setPassword", inst._setPassword);
        	
        	// make sure ReadOnly properties don't have setters
        	msg = "ReadOnly properties shouldn't have setters."
        	assertUndefined(msg + "setAge", inst.setAge);
        	
        	// make sure WriteOnly properties don't have getters
        	msg = "WriteOnly properties shouldn't have getters."
        	assertUndefined(msg + "getSalt", inst.getSalt);
        	
        	// test chaining
        	assertEquals("Return of a setter should be the same instance to allow chaining.", 
        		inst, inst.setFirstName("Mat"));
        	assertEquals("Return of a setter should be the same instance to allow chaining.", 
        		inst, inst._setPassword("secret"));
        	
        	// test the getters and setters
        	assertEquals("Mat", inst.getFirstName());
        	assertEquals("secret", inst._getPassword());
        	
        	// make sure the original properties are NOT there and are
        	// replaced with 'null' instead.
        	msg = "Original properties should be replaced with 'null' value to prevent confusion.";
        	assertNull(msg, inst.lastName);
        	
        }
        
    </script>
</head>
<body>
</body>
</html>